package net.qiqbframework.commandhandling.callbacks;


import net.qiqbframework.commandhandling.CommandCallback;
import net.qiqbframework.commandhandling.CommandMessage;
import net.qiqbframework.commandhandling.CommandResultMessage;
import net.qiqbframework.commandhandling.GenericCommandResultMessage;

import javax.annotation.Nonnull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static net.qiqbframework.commandhandling.GenericCommandResultMessage.asCommandResultMessage;

/**
 * Command Handler Callback，允许调度线程使用 Future 机制等待回调的结果。此回调允许调用方在使用异步命令总线时同步调用。
 */
public class FutureCallback<C, R> extends CompletableFuture<CommandResultMessage<? extends R>>
        implements CommandCallback<C, R> {

    @Override
    public void onResult(@Nonnull CommandMessage<? extends C> commandMessage,
                         @Nonnull CommandResultMessage<? extends R> commandResultMessage) {
        super.complete(commandResultMessage);
    }

    public CommandResultMessage<? extends R> getResult() {
        try {
            return get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new GenericCommandResultMessage<>((R) null);
        } catch (ExecutionException e) {
            return asCommandResultMessage(e.getCause());
        } catch (Exception e) {
            return asCommandResultMessage(e);
        }
    }

    public CommandResultMessage<? extends R> getResult(long timeout, TimeUnit unit) {
        try {
            return get(timeout, unit);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return new GenericCommandResultMessage<>(e);
        } catch (ExecutionException e) {
            return asCommandResultMessage(e.getCause());
        } catch (Exception e) {
            return asCommandResultMessage(e);
        }
    }


    public boolean awaitCompletion(long timeout, TimeUnit unit) {
        try {
            get(timeout, unit);
            return true;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        } catch (ExecutionException e) {
            return true;
        } catch (TimeoutException e) {
            return false;
        }
    }
}
